home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
System Booster
/
System Booster.iso
/
Archives
/
Timing
/
clock1_2.lha
/
clock1_2
/
compute_date.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-05-16
|
6KB
|
165 lines
/* This code comes from a posting by Tomas Rokicki to the old
* comp.sys.amiga newsgroup in August of 1988. Since it was publicly
* posted and there is no copyright, I'm assuming it's ok to use this
* code. This is pretty clever stuff!
*
* I've renamed this function to compute_date(), and made it return
* the year, month, day, and weekday number, rather than setting
* globals. I've also modified the code a little to do this, and to
* use 0-based day, month, and weekday, and to compute day of year.
* If it doesn't work, it's almost certainly my fault.
*
*
* Here's Tomas' original opening comment:
*
*
* Written by Tomas Rokicki of Radical Eye Software.
*
* Here's an updated ShowDate() routine, as requested by Rob
* Peck. This one even has some limited explanations of how
* it works! It sets the day of week as well as month, day,
* and year from the values returned by DateStamp().
*
* Note that the original code published in Peck's book, would
* fail for dates prior to 1984. When compiled with 16-bit
* integers, it would fail for dates after 2007. The current
* code will work with any size integer from Amiga day 0 until
* March 1, 2100.
*/
#ifndef OLDTIME
/*
* This routine returns in *m, *d, *y, *w, and *jul the current
* month, day, year, day of week, and day of year. Months, days,
* and day of weeks all start at 1 with first members January, the
* first, and Sunday, respectively.
*/
void compute_date(long n, int *y, int *m, int *d, int *w, int *jul)
{
int tmp_y, tmp_m, ly;
/*
* Set n to the number of days since Amiga day -671, which is
* March 1, 1976. It's easier to figure years starting in March,
* since then the lengths of the months are 31, 30, 31, 30, 31,
* 31, 30, 31, 30, 31, 31, 28. This is almost linear.
*/
n += 671 ;
/*
* The easiest is the weekday. This is simply the number of days
* modulo 7, corrected for the start date. March 1, 1976 was a
* Monday, so we add 1 to get back to a Sunday, and take the modulo.
*/
*w = (n + 1) % 7 ;
/*
* There are exactly 1461 days every four years, until 2100, which
* is the first year divisible by 4 that is not a leap year AA
* (After Amiga.) This gives the years lengths of 365 (1976),
* 365 (1977), 365 (1978), and 366 (1979). Note that this is
* correct because we start our years in March, so 1979 is the
* leap year.
*/
tmp_y = (4 * n + 3) / 1461 ;
/*
* We now subtract off the years (see them melt off her face.)
* We use a long constant for 16-bit systems. Again we use the
* fact that the leap year is the fourth year, not the first.
*/
n -= 1461L * tmp_y / 4 ;
/*
* Now we can adjust the year to the proper value by adding
* 1976.
*/
tmp_y += 1976 ;
/*
* We're now in a position to calculate the day of the year. At this
* point, n is the day of the year with 1 March = 0. To get the
* actual day of year, we need to add the number of days in Jan and
* Feb, and take this modulo the number of days in that year. There
* are 59 days in Jan and Feb, 60 in leap years, and 365 days per
* year, 366 in leap years. So lets figure out if this is a leap year
* first. (There is a catch here: our idea of the year may be wrong.
* Looking below, you see we increment the year if the month is Jan or
* Feb. Actually, though, that's what we want, because we only add
* the extra day after Feb in a leap year.)
*/
ly = (tmp_y%4) == 0;
*jul = (n + 59 + ly) % (365 + ly);
/*
* We calculate the month. Since we start in March, the length
* of the months are always 30 or 31, except for the last month,
* which is shorter. This is fortunate, as it allows us to use
* a simple mathematical formula for the month. The lengths of
* the months are (31, 30, 31, 30, 31), repeated three times and
* the end lopped off. So, our slope is 153/5. An intercept of
* 2 gives us the 31 and 30 lengths.
*/
tmp_m = (5 * n + 2) / 153 ;
/*
* And now we subtract off the months.
*/
*d = n - (153 * tmp_m + 2) / 5 ;
/*
* Now we convert from March-based years back to January-based
* years.
*/
tmp_m += 2 ;
/*
* And, if we've gone over 12, we increment the year.
*/
if (tmp_m > 11) {
tmp_y++ ;
tmp_m -= 12 ;
}
/*
* That's it!
*/
*y = tmp_y;
*m = tmp_m;
}
#else
/* Here's my old implementation. It's easier to understand, but
* bigger and slower.
*/
#define FOUR_YEARS (4*365+1)
/* Jan 1 is day 0, so Feb 29 is day 31+28 = 59 */
#define LEAP_DAY 59
/* weekday (Sunday=0) of 1-Jan-76 */
#define WKDAY_BIAS 4
void compute_date(long n, int *y, int *m, int *d, int *w, int *jul)
{
/* unsigned shorts are cheaper for maths than ints */
unsigned short days = n + 365*2 + 1;
unsigned short yr4 = days/FOUR_YEARS;
unsigned short dy4 = days%FOUR_YEARS;
unsigned short dy;
*w = (days+WKDAY_BIAS)%7; /* easy */
if (dy4 == LEAP_DAY) {
*jul = dy4;
*d = 28; /* 29 Feb, 0 origin */
*m = 1; /* February, 0 origin */
*y = 1976 + 4*yr4;
} else {
static unsigned char days_in_month[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
unsigned char *ptr;
if (dy4 > LEAP_DAY) --dy4;
/* now we can forget 29 Feb ever happens */
*y = 1976 + (dy4/365) + (yr4*4);
*jul = dy = dy4 % 365;
/* dy is now day of year */
for (ptr=days_in_month; dy>=*ptr; ++ptr) dy -= *ptr;
*m = ptr-days_in_month;
*d = dy;
}
}
#endif